package cn.bugio.oauth1demo.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.HashMap;
import java.util.Map;

/**
 * <h1>配置Security</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2021/08/28
 */
@Configuration
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final ObjectMapper objectMapper;


    //                    //.loginPage("/login") //自定义登入地址
//                    //.loginProcessingUrl("/login") //自定义未登入返回页面
//                    .successHandler(loginSuccessHandler) //登入成功处理返回数据
//                    .failureHandler(loginFailureHandler) //登入失败处理返回数据

//    //传统写法
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.formLogin() //配置启用登入页面 .httpBasic() 基础登入模式
//                .and()
//                .authorizeRequests()//配置全部接口需要权限
//                //.antMatchers("/login").permitAll()
//                .anyRequest().authenticated()
//                .and()
//                .logout().permitAll() //放行登出接口
//                .and()
//                .csrf().disable() //关闭跨域
//                .headers().frameOptions().disable(); //放行options请求
//    }

    //新写法
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeRequests(req-> req.anyRequest().authenticated())
//                .formLogin(form -> form.loginPage("/login")
//                        .successHandler((req,resp,auth)->{
//                            //返回json格式字符串
//                            ObjectMapper objectMapper = new ObjectMapper();
//                            resp.setStatus(HttpStatus.OK.value());
//                            resp.setContentType(MediaType.APPLICATION_JSON_VALUE);
//                            resp.setCharacterEncoding("UTF-8");
//                            resp.getWriter().print(objectMapper.writeValueAsString(auth)); //自定义返回结果
//
//                        })
//                        .failureHandler((req,resp,exp)->{
//                            //返回json格式字符串 val 代替 final ObjectMapper
//                            val objectMapper = new ObjectMapper();
//                            resp.setStatus(HttpStatus.UNAUTHORIZED.value());
//                            resp.setContentType(MediaType.APPLICATION_JSON_VALUE);
//                            resp.setCharacterEncoding("UTF-8");
//                            Map<String,String> map = new HashMap<>();
//                            map.put("msg","认证失败");
//                            map.put("datails",exp.getMessage());
//                            resp.getWriter().print(objectMapper.writeValueAsString(map)); //自定义返回结果
//                        })
//                        .permitAll()) //配置登入表单
//                .logout(logout -> logout.permitAll()) //放行登出接口
//                .csrf(Customizer.withDefaults())//默认实现
////                .rememberMe(Customizer.withDefaults())//默认实现
//                .rememberMe(rememberMe -> rememberMe.tokenValiditySeconds(30*24*3600).rememberMeCookieName("someKeyToRemember"))
//                .headers(headers -> headers.frameOptions().disable()); //放行options请求
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(req-> req
                .antMatchers("/authorize/**").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/api/**").hasRole("USER")
                .anyRequest()
                .authenticated())
                .addFilterAt(restAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .csrf(csrf -> csrf.ignoringAntMatchers("/authorize/**"))
                .headers(headers -> headers.frameOptions().disable()); //放行options请求
    }

    /**
     * 将失败处理函数式封装
     * @return
     */
    private AuthenticationFailureHandler getAuthenticationFailureHandler() {
        return (req, resp, exp) -> {
            resp.setStatus(HttpStatus.UNAUTHORIZED.value());
            resp.setContentType(MediaType.APPLICATION_JSON_VALUE);
            resp.setCharacterEncoding("UTF-8");
            Map<String, String> map = new HashMap<>();
            map.put("msg", "认证失败");
            map.put("datails", exp.getMessage());
            resp.getWriter().print(objectMapper.writeValueAsString(map)); //自定义返回结果
        };
    }

    /**
     * 将成功处理函数式封装
     * @return
     */
    private AuthenticationSuccessHandler getAuthenticationSuccessHandler() {
        return (req, resp, auth) -> {
            resp.setStatus(HttpStatus.OK.value());
            resp.setContentType(MediaType.APPLICATION_JSON_VALUE);
            resp.setCharacterEncoding("UTF-8");
            resp.getWriter().print(objectMapper.writeValueAsString(auth)); //自定义返回结果

        };
    }

    /**
     * 创建自定义设置过滤器
     * @return
     * @throws Exception
     */
    private RestAuthenticationFilter restAuthenticationFilter() throws Exception {
        RestAuthenticationFilter filter = new RestAuthenticationFilter(objectMapper);
        //设置成功处理器
        filter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler());
        //设置失败处理器
        filter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());
        //设置认证管理器
        filter.setAuthenticationManager(authenticationManager());
        filter.setFilterProcessesUrl("/authorize/login");
        return filter;
    }


    /**
     * 配置内存用户
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user")
                .password(passwordEncoder().encode("123456")) //加密后结构 {bcrypt}$2a$10$dbaRNmrkEv8n4ESpq1IWD.lmZKJqTpDSGEPNewaRP3GPsl4xi6mdW
                .roles("USERS","ADMIN")
                .and()
                .withUser("tom")
                //{id}encodedPassword
                .password("{SHA-1}"+new MessageDigestPasswordEncoder("SHA-1").encode("123456"))
                .roles("USERS");
    }

    /**
     * 配置多密码编码
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        val idForDefault = "bcrypt";
        Map encoders = new HashMap();
        encoders.put(idForDefault,new BCryptPasswordEncoder());
        encoders.put("SHA-1",new MessageDigestPasswordEncoder("SHA-1"));
        return new DelegatingPasswordEncoder(idForDefault,encoders);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }

}